package operations;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;


import util.Randomness;
import model.CUT;
import model.ConstructorDeclared;
import model.Filter;
import model.TestClusterBuilder;
import model.StatementKind;
public class CreateObject extends Operation implements Cloneable{

	private  String ref;
	private final StatementKind constructor;
	private final List<Object> values;


	public CreateObject(String ref, StatementKind constructor, List<Object>  values) {
		this.ref = ref;
		this.constructor = constructor;
		this.values = values;
	}
	public List<Object> getValues(){
		 return this.values;	 
	 }
	public String getRef(){
		 return this.ref;	 
	 }
	public void setRefrence(String ref){
		  this.ref=ref;	 
	 }
	
	 public void setValues(List<Object> newValues){
		  this.values.clear();
		  this.values.addAll(newValues);
	 }


	public static CreateObject generate(CUT cut, TestClusterBuilder TestCluster, String ref){
//		if(cut.getClazz().getName().equals("nanoxml.StdXMLParser"))
//			System.out.println("CreateObject.generate: "+cut.getClazz() );
		try{
		Map<String, String[]> refFactory = TestCluster.getRefFactory();
		Class<?> clazz=cut.getClazz();
		if(Filter.isPrimitiveOrStringType(clazz))return null;
		if(Filter.isAbstract(clazz)){
			clazz=generateReference(clazz,TestCluster);
			if(clazz==null) return null;
			cut=TestCluster.getCluster(clazz.getName());
		}
		if(cut.getConstructors().length==0) return null;
		StatementKind constructor = cut.getConstructors()[Randomness.nextRandomInt(cut.getConstructors().length)];
		List<Object> values=selectInputs(constructor,TestCluster);
		if(values==null)return null;
		if(ref==null)
			ref = getReference(cut.getClazz().getName(),refFactory);
		return new CreateObject(ref,constructor,values);
	}catch (Throwable e) {
		System.out.println("CreateObject.generate: error "+cut.getClazz() + " " + e.getMessage());
		return null;
	}


	}
	public static CreateObject recommendedgenerate(Class<?> clz,StatementKind constr, TestClusterBuilder TestCluster){
		if(!(constr instanceof ConstructorDeclared)) return null;
		
		StatementKind constructor =constr;// clazz.getConstructors()[Randomness.nextRandomInt(clazz.getConstructors().length)];
		List<Object> values=selectInputs(constructor,TestCluster);
		
		Map<String, String[]> refFactory = TestCluster.getRefFactory();
		String ref = getReference(clz.getName(),refFactory);

		return new CreateObject(ref,constructor,values);
	}
	public static List<Object> selectInputs(StatementKind statement,TestClusterBuilder TestCluster){
//		if(statement.toString().contains("java.util.Vector"))
//			System.out.println("CreateObejct.SelectInputs: " + statement);
		 Map<String, String[]> refFactory=TestCluster.getRefFactory();
		   List<Class<?>> inputClasses = statement.getInputTypes();
		   List <Object> values=new LinkedList<Object>();
		   for (int i = 0; i < inputClasses.size(); i++) {
			      Class<?> paramTpe = inputClasses.get(i);

					if (Filter.isPrimitiveOrStringType(paramTpe))
						values.add(ValuesGenerator.generatePrimitiveValue(paramTpe));
					else{
						if(Filter.isObjectType(paramTpe)){
							paramTpe=generateReference(paramTpe, TestCluster);
							if (Filter.isPrimitiveOrStringType(paramTpe))
								values.add(ValuesGenerator.generatePrimitiveValue(paramTpe));
							else{
								if  (ValuesGenerator.getBoolean(Operation.SET_TO_NULL)||(paramTpe==null))
									values.add(null);
								else
									values.add(Operation.getReference(paramTpe.getName(), refFactory));
							}
						}
						else{
							paramTpe=generateReference(paramTpe, TestCluster);
							if  (ValuesGenerator.getBoolean(Operation.SET_TO_NULL)||(paramTpe==null))
								values.add(null);
							else
								values.add(Operation.getReference(paramTpe.getName(), refFactory));
						}
					}
			}
		return values;
		
	}

	public StatementKind getConstructor(){
		return this.constructor;
	}
 @Override
	public CreateObject clone() {
	 try{
		List<Object> cloneValues = new ArrayList<Object>(values);
		final CreateObject clone = new CreateObject(ref,constructor,cloneValues);
		clone.addInfo(getInfos());
		return clone;

		}catch( Throwable e){
		     System.err.println("CreateObject.clone:  "+e);
		     System.exit(1);
	    	 
	     }
		return null;
	}
	@Override
	public String toString() {
	     StringBuilder sb = new StringBuilder();

		try{
	     StatementKind CUT =this.constructor;
    	 sb.append(" "+this.ref + " = new ");
	     sb.append(((ConstructorDeclared) CUT).getConstructor().getName() +"(");
	     List<Object> values = this.getValues();
	     List<Class<?>> inputType =((ConstructorDeclared) CUT).getInputTypes();
	     for(int j=0;j<values.size();j++){
	    	 Class<?> valueClass=inputType.get(j);
	    	 Object value=values.get(j);
	    	 sb.append(ValuesGenerator.toCodeString(valueClass,value));
	         if (j < (values.size() - 1))
	           sb.append(",");
	       }

	     sb.append(");");
		}catch( Throwable e){
		     System.err.println("CreateObject.toString " + sb);
		     System.err.println("values " + values );
	    	 
	     }
	     return sb.toString();

	}

}